﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MikuMikuLibraryNew.Archives;
using MikuMikuLibraryNew.IO;
using MikuMikuLibraryNew;
using MikuMikuLibraryNew.Objects.Processing;
using MikuMikuModel.Logs;
using MikuMikuLibraryNew.Objects;
using MikuMikuLibraryNew.Databases;
using System.IO;
using models = ft_module_parser.aftconversion.divax.models_v2;

namespace ft_module_parser.aftconversion.divax
{
    class objset_v2
    {
        public const int cachever = 3;
        ushort FindUnusedID(StageDatabase staged, StageDatabase staged2)
        {
            if (staged2 == null)
            {
                return FindUnusedID(staged);
            }
            ushort id = 0;

            var stage = staged.Stages.Where(c => c.Id == 0).FirstOrDefault();
            var stage2 = staged2.Stages.Where(c => c.Id == 0).FirstOrDefault();
            while ((stage != null) || (stage2 != null))
            {
                id++;
                stage = staged.Stages.Where(c => c.Id == id).FirstOrDefault();
                stage2 = staged2.Stages.Where(c => c.Id == id).FirstOrDefault();
            }
            Logs.WriteLine("new stage at id = " + id);
            return id;
        }
        ushort FindUnusedID(StageDatabase staged)
        {
            ushort id = 0;

            var stage = staged.Stages.Where(c => c.Id == 0).FirstOrDefault();
            while (stage != null)
            {
                id++;
                stage = staged.Stages.Where(c => c.Id == id).FirstOrDefault();
            }
            Logs.WriteLine("new stage at id = " + id);
            return id;
        }
        ushort FindUnusedID(ObjectDatabase staged, ObjectDatabase staged2)
        {
            if (staged2 == null)
            {
                return FindUnusedID(staged);
            }
            ushort id = 0;

            var stage = staged.ObjectSets.Where(c => c.Id == 0).FirstOrDefault();
            var stage2 = staged2.ObjectSets.Where(c => c.Id == 0).FirstOrDefault();
            while ((stage != null) || (stage2 != null))
            {
                id++;
                stage = staged.ObjectSets.Where(c => c.Id == id).FirstOrDefault();
                stage2 = staged2.ObjectSets.Where(c => c.Id == id).FirstOrDefault();
            }

            //id = (ushort)(staged.Objects.Max(c => c.Id) + 1);
            Logs.WriteLine("new object at id = " + id);
            return id;
        }

        ushort FindUnusedID(ObjectDatabase staged)
        {
            ushort id = 0;

            var stage = staged.ObjectSets.Where(c => c.Id == 0).FirstOrDefault();
            while (stage != null)
            {
                id++;
                stage = staged.ObjectSets.Where(c => c.Id == id).FirstOrDefault();
            }

            //id = (ushort)(staged.Objects.Max(c => c.Id) + 1);
            Logs.WriteLine("new object at id = " + id);
            return id;
        }

        public models_v2 Stripify(string filePath, BinaryFormat format = BinaryFormat.FT)
        {
            var stgpv = new ObjectSet();
            var textures = new MikuMikuLibraryNew.Textures.TextureSet();
            var texdb = new TextureDatabase();
            bool fromCache = false;

            using (var farcArchive = BinaryFile.Load<FarcArchive>(filePath))
            {
                using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txi")).First(), EntryStreamMode.MemoryStream))
                    texdb.Load(entryStream);
                using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txd")).First(), EntryStreamMode.MemoryStream))
                    textures.Load(entryStream);
                using (var entryStream = farcArchive.Open(farcArchive.Entries.First(), EntryStreamMode.MemoryStream))
                    stgpv.Load(entryStream, textures, texdb);
            }

            int pvid = int.Parse(Path.GetFileNameWithoutExtension(filePath).Substring(5, 3));
            int pvid2 = pvid + 800;

            if (Path.GetFileName(filePath).Contains("hrc"))
            {
                var newtexset = new MikuMikuLibraryNew.Textures.TextureSet();
                foreach (var i in stgpv.TextureSet.Textures)
                    newtexset.Textures.Add(i);
                if (File.Exists(Path.GetDirectoryName(filePath) + "\\" + "effpv" + pvid2 + ".farc"))
                {
                    using (var farcArchive = BinaryFile.Load<FarcArchive>(Path.GetDirectoryName(filePath) + "\\" + "effpv" + pvid2 + ".farc"))
                    {
                        var effpv = new ObjectSet();
                        var texdbtemp = new TextureDatabase();
                        var textemp = new MikuMikuLibraryNew.Textures.TextureSet();
                        using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txi")).First(), EntryStreamMode.MemoryStream))
                            texdbtemp.Load(entryStream);
                        using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txd")).First(), EntryStreamMode.MemoryStream))
                            textemp.Load(entryStream);
                        using (var entryStream = farcArchive.Open(farcArchive.Entries.First(), EntryStreamMode.MemoryStream))
                            effpv.Load(entryStream, textemp, texdbtemp);

                        for (int i = 0; i < effpv.Objects.Count; i++)
                            stgpv.Objects.Add(effpv.Objects[i]);

                        foreach (var i in effpv.TextureSet.Textures)
                            newtexset.Textures.Add(i);

                        for (int i = 0; i < effpv.TextureIds.Count; i++)
                            stgpv.TextureIds.Add(effpv.TextureIds[i]);
                    }


                    Logs.WriteLine("Merged effpv");
                }
                if (File.Exists(Path.GetDirectoryName(filePath) + "/" + "effpv" + pvid2 + "ptc.farc"))
                {
                    
                    using (var farcArchive = BinaryFile.Load<FarcArchive>(Path.GetDirectoryName(filePath) + "/" + "effpv" + pvid2 + "ptc.farc"))
                    {
                        var effpvptc = new ObjectSet();
                        var texdbtemp = new TextureDatabase();
                        var textemp = new MikuMikuLibraryNew.Textures.TextureSet();
                        using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txi")).First(), EntryStreamMode.MemoryStream))
                            texdbtemp.Load(entryStream);
                        using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txd")).First(), EntryStreamMode.MemoryStream))
                            textemp.Load(entryStream);
                        using (var entryStream = farcArchive.Open(farcArchive.Entries.First(), EntryStreamMode.MemoryStream))
                            effpvptc.Load(entryStream, textemp, texdbtemp);

                        for (int i = 0; i < effpvptc.Objects.Count; i++)
                            stgpv.Objects.Add(effpvptc.Objects[i]);

                        foreach (var i in effpvptc.TextureSet.Textures)
                            newtexset.Textures.Add(i);

                        for (int i = 0; i < effpvptc.TextureIds.Count; i++)
                            stgpv.TextureIds.Add(effpvptc.TextureIds[i]);

                    }
                }
                newtexset.Endianness = stgpv.Endianness;
                newtexset.Format = stgpv.Format;

                stgpv.TextureSet = newtexset;
            }

            if (File.Exists("x_obj_cache_v" + cachever + "/" + Path.GetFileNameWithoutExtension(filePath)))
            {
                var cached_model = new models_v2();
                cached_model.fileName = filePath;
                cached_model.model = new ObjectSet();
                cached_model.model.Load("x_obj_cache_v" + cachever + "/" + Path.GetFileNameWithoutExtension(filePath));
                cached_model.model.Format = format;
                stgpv.Objects.Clear();
                foreach (var mesh in cached_model.model.Objects)
                {
                    stgpv.Objects.Add(mesh);
                }
                Logs.WriteLine("Loaded from Cache " + Path.GetFileName(filePath));
                fromCache = true;
            }
            else foreach (var meshes in stgpv.Objects)
                {
                    meshes.Meshes.RemoveAll(x => x.Vertices == null || x.Vertices.Length == 0);
                    foreach (var submeshes in meshes.Meshes)
                    {
                        foreach (var indexTable in submeshes.SubMeshes)
                        {
                            ushort[] triangleStrip = Stripifier.Stripify(indexTable.Indices);
                            if (triangleStrip != null)
                            {
                                indexTable.PrimitiveType = PrimitiveType.TriangleStrip;
                                indexTable.Indices = triangleStrip;
                            }
                        }
                    }
                }
            stgpv.Format = format;
            var le_model = new models_v2();
            le_model.model = stgpv;
            le_model.fileName = filePath;
            if (filePath.Contains("effpv"))
            {
                le_model.fileName = Path.GetDirectoryName(filePath) + "/stgpv" + (pvid2 - 800) + "hrc.farc";
                Logs.WriteLine("effpv " + Path.GetFileName(le_model.fileName));
            }
            try
            {
                if (!File.Exists("x_obj_cache_v" + cachever + "/" + Path.GetFileNameWithoutExtension(filePath)))
                    le_model.model.Save("x_obj_cache_v" + cachever + "/" + Path.GetFileNameWithoutExtension(filePath));
            }
            catch (Exception Ex)
            {
                Logs.WriteLine("Stripifier : Failed to write cache " + Path.GetFileNameWithoutExtension(filePath) + "\n" + Ex.Message + " " + Ex.StackTrace);
            }
            //File.WriteAllText(le_model.fileName, "x_obj_cache/" + Path.GetFileNameWithoutExtension(filePath) + ".meta");
            if (!fromCache)
                Logs.WriteLine("Stripified " + Path.GetFileName(le_model.fileName));
            return le_model;
        }

        public void GenerateObjSet(string filePath, ObjectSet stgpv, ObjectDatabase objdb, ObjectDatabase objdb2, TextureDatabase texturedb, StageDatabase staged, StageDatabase staged2, string acpath, divamodgen divamods, bool doProcess = true, bool debug = false, bool isConsole = false)
        {
            /*
            var stgpv = new Model();
            var textures = new MikuMikuLibrary.Textures.TextureSet();
            var texdb = new TextureDatabase();

            using (var farcArchive = BinaryFile.Load<FarcArchive>(filePath))
            using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txi")).First(), EntryStreamMode.MemoryStream))
                texdb.Load(entryStream);

            if (debug)
            {
                string farcpath = acpath + "\\rom\\objset\\" + Path.GetFileNameWithoutExtension(filePath).Replace("stgpv0", "stgpv8") + ".farc";

                using (var farcArchive = BinaryFile.Load<FarcArchive>(farcpath))
                using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("tex")).First(), EntryStreamMode.MemoryStream))
                    textures.Load(entryStream);

            }
            else
            {
                using (var farcArchive = BinaryFile.Load<FarcArchive>(filePath))
                using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txd")).First(), EntryStreamMode.MemoryStream))
                    textures.Load(entryStream);

                using (var farcArchive = BinaryFile.Load<FarcArchive>(filePath))
                using (var entryStream = farcArchive.Open(farcArchive.Entries.First(), EntryStreamMode.MemoryStream))
                    stgpv.Load(entryStream, textures, texdb);
            }

            */

            if (Path.GetFileNameWithoutExtension(filePath).Contains("effpv"))
            {
                filePath = Path.GetDirectoryName(filePath) + "\\" + Path.GetFileNameWithoutExtension(filePath).Replace("effpv", "stgpv") + "hrc2.farc";
            }

            var texdb = new TextureDatabase();

            //using (var farcArchive = BinaryFile.Load<FarcArchive>(filePath))
            //using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txi")).First(), EntryStreamMode.MemoryStream))
            //   texdb.Load(entryStream);

            var objentry = new ObjectSetInfo();
            objentry.ArchiveFileName = Path.GetFileName(filePath).Replace("stgpv0", "stgpv8");
            objentry.FileName = Path.GetFileNameWithoutExtension(filePath).Replace("stgpv0", "stgpv8") + "_obj.bin";
            objentry.Id = FindUnusedID(objdb, objdb2);
            objentry.Name = Path.GetFileNameWithoutExtension(filePath).Replace("stgpv0", "stgpv8").ToUpper();
            objentry.TextureFileName = Path.GetFileNameWithoutExtension(filePath).Replace("stgpv0", "stgpv8") + "_tex.bin";

            int ground_id = -1;

            ushort counter = 1;
            /*
            foreach (var tset in stgpv.TextureSet.Textures)
            {
                tset.Name = tset.Name.Replace("STGPV0", "STGPV8").ToUpper();
            }
            */
            foreach (var meshes in stgpv.Objects)
            {
                //meshes.SubMeshes.RemoveAll(x => x.Vertices == null || x.Vertices.Length == 0);
                meshes.Name = meshes.Name.Replace("STGPV0", "STGPV8");
                meshes.Name = meshes.Name.Replace("EFFPV", "STGPV");

                if (Path.GetFileName(filePath).Contains("hrc"))
                {
                    int pvid = int.Parse(Path.GetFileName(filePath).Substring(5, 3));

                    if (pvid < 200)
                    {
                        pvid = pvid + 800;
                    }

                    {
                        var check2 = divamods.Divamods.Where(c => c.pvid == pvid).FirstOrDefault();
                        if (check2 == null)
                        {
                            divamods.Divamods.Add(new aftconversion.divamods(pvid));
                            Logs.WriteLine("objset: Created new PV at id " + pvid);
                            check2 = divamods.Divamods.Where(c => c.pvid == pvid).First();
                        }
                        check2.item_pv.Add(meshes.Name);
                        Logs.WriteLine("objset: Added item_pv for PV at id " + pvid + "," + meshes.Name, 1);
                    }
                }

                var meshentry = new ObjectInfo();
                meshes.Id = counter;
                meshentry.Id = (ushort)meshes.Id;
                meshentry.Name = meshes.Name;

                if (meshes.Name.Contains("GND"))
                    ground_id = meshes.Id;

                objentry.Objects.Add(meshentry);

                if (doProcess)
                {

                    if (!debug)
                    {
                        /*
                        foreach (var submeshes in meshes.SubMeshes)
                        {
                            foreach (var indexTable in submeshes.IndexTables)
                            {
                                ushort[] triangleStrip = Stripifier.Stripify(indexTable.Indices);
                                if (triangleStrip != null)
                                {
                                    indexTable.PrimitiveType = PrimitiveType.TriangleStrip;
                                    indexTable.Indices = triangleStrip;
                                }
                            }
                        }
                        */
                    }

                }
                counter++;
            }

            stgpv.TextureSet.Format = BinaryFormat.DT;
            stgpv.Format = BinaryFormat.DT;

            if (doProcess)
                stgpv.Save("temp\\" + Path.GetFileNameWithoutExtension(filePath) + "_obj.bin", null, texturedb, null);
            objdb.ObjectSets.Add(objentry);

            if (Path.GetFileNameWithoutExtension(filePath).Count() == 8)
            {
                Stage stage = new Stage();
                stage.Id = FindUnusedID(staged, staged2);
                stage.Name = Path.GetFileNameWithoutExtension(filePath).Replace("stgpv0", "stgpv8").ToUpper();

                stage.Objects[0].Id = (short)objentry.Id;
                stage.Objects[1].Id = (short)ground_id;
                stage.Objects[1].SetId = (short)objentry.Id;
                stage.Objects[2].Id = -1;
                stage.Objects[2].SetId = -1;
                stage.Objects[3].Id = -1;
                stage.Objects[3].SetId = -1;
                stage.Objects[4].Id = -1;
                stage.Objects[4].SetId = -1;
                stage.Objects[5].Id = -1;
                stage.Objects[5].SetId = -1;
                stage.Objects[6].Id = -1;
                stage.Objects[6].SetId = -1;

                //stage.ObjectGroundId = (short)ground_id;
                //stage.ObjectGroundIdFlag = (short)objentry.Id;

                //stage.ObjectId3 = -1;
                //stage.ObjectIdFlag3 = -1;
                //stage.ObjectId5 = -1;
                //stage.ObjectIdFlag5 = -1;
                //stage.ObjectId7 = -1;
                //stage.ObjectIdFlag7 = -1;
                stage.RingRectangleX = -8;
                stage.RingRectangleY = -8;
                stage.RingRectangleWidth = 16;
                stage.RingRectangleHeight = 16;
                stage.RingRingHeight = 0;
                stage.RingOutHeight = -1000;
                stage.Field00 = 1;
                stage.Field11 = -1;

                var fbtex = stgpv.TextureSet.Textures.Where(c => c.Name.Contains("_AM_FB05")).FirstOrDefault();
                if (fbtex != null)
                    stage.Field02 = (int)fbtex.Id;
                else
                    stage.Field02 = -1;

                stage.LensFlareScaleX = -1;
                stage.LensFlareScaleY = -1;
                stage.LensFlareScaleZ = -1;

                //stage.ObjectSkyId = -1;
                //stage.ObjectSkyIdFlag = -1;
                //stage.ObjectReflectId = -1;
                //stage.ObjectReflectIdFlag = -1;

                stage.CollisionFilePath = @"rom/STGTST_COLI.000.bin";

                //stage.ObjectId1 = (short)objentry.Id;
                //stage.StageEffect1 = StageEntry.StageEffect.Empty;
                stage.Auth3dName = "EFF" + stage.Name;

                staged.Stages.Add(stage);
            }

            if (doProcess)
            {
                var newfarc = new FarcArchive();
                newfarc.Add(Path.GetFileNameWithoutExtension(filePath).Replace("stgpv0", "stgpv8") + "_obj.bin", "temp\\" + Path.GetFileNameWithoutExtension(filePath) + "_obj.bin");
                newfarc.Add(Path.GetFileNameWithoutExtension(filePath).Replace("stgpv0", "stgpv8") + "_tex.bin", "temp\\" + Path.GetFileNameWithoutExtension(filePath) + "_tex.bin");
                newfarc.Alignment = 16;
                newfarc.IsCompressed = false;
                if (isConsole)
                    newfarc.Save(acpath + "\\rom\\objset\\" + Path.GetFileNameWithoutExtension(filePath).Replace("stgpv0", "stgpv8") + ".farc");
                else
                    newfarc.Save(acpath + "\\mdata\\M999\\rom\\objset\\" + Path.GetFileNameWithoutExtension(filePath).Replace("stgpv0", "stgpv8") + ".farc");

                File.Delete("temp\\" + Path.GetFileNameWithoutExtension(filePath) + "_obj.bin");
                File.Delete("temp\\" + Path.GetFileNameWithoutExtension(filePath) + "_tex.bin");
            }


        }
    }

}
